package com.uxsino.simo.indicator.retractor;

import java.util.HashMap;
import java.util.Map;

import com.uxsino.simo.networkentity.EntityInfo;
import com.uxsino.simo.query.QueryTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uxsino.commons.utils.config.PropElement;
import com.uxsino.simo.indicator.Indicator;
import com.uxsino.simo.query.QueryContext;
import com.uxsino.simo.utils.ConfigLoadingContext;

/**
 * 
 * 
 * extract key-value pairs from redis.
 * example query command result:
 * # CPU
 *used_cpu_sys:0.14
 *used_cpu_user:0.14
 *used_cpu_sys_children:0.00
 *used_cpu_user_children:0.00
 *
 * # Cluster
 *cluster_enabled:0
 *
 * <retract parser="redis_text" indicator="...">
 * <field name="..." key_name="..." parser="..." pattern="..."/>
 * where name is the one used in indicator definition
 * key_name is the key used in the text key value pair
 * example:
 * <field name="my_cpu_usage" key_name="used_cpu_sys" />
 *
 */
public class RedisKVRetractor extends CompoundValueRetractor {
    private static Logger logger = LoggerFactory.getLogger(RedisKVRetractor.class);

    private Map<String, String> fieldKeyNameMap = new HashMap<>();

    @Override
    public Object doRetract(EntityInfo entity, QueryContext ctxt, QueryTemplate qt, Object obj) {
        if (obj == null) {
            logger.error("retract input is null");
            return null;
        }
        Map<String, Object> values = new HashMap<>(fieldRetrievers.size());
        Map<String, String> infoMap = getAllInfo(obj);
        for (Map.Entry<String, IValueRetractor> entry : fieldRetrievers.entrySet()) {
            String keyName = fieldKeyNameMap.get(entry.getKey());
            Object value = null;
            if (null == keyName) {
                logger.error("key_name should not be null for {} ", entry.getKey());
                continue;
            }
            if (infoMap.containsKey(keyName)) {
                value = entry.getValue().retract(entity, ctxt, qt, infoMap.get(keyName));
            } else {
                logger.error("cannot get info of {}", entry.getKey());
            }
            values.put(entry.getKey(), value);
        }
        return values;
    }

    private Map<String, String> getAllInfo(Object obj) {
        Map<String, String> infoMap = new HashMap<>();
        String[] strs = ((String) obj).split("[\r\n]+");
        for (String line : strs) {
            if (line.indexOf(':') != -1) {
                String[] lineArr = line.split(":");
                if (lineArr.length == 2) {
                    infoMap.put(lineArr[0], lineArr[1]);
                } else if (lineArr.length == 1) {
                    infoMap.put(lineArr[0], "");
                }
            }
        }
        return infoMap;
    }

    public void addFieldKeyName(String fieldName, String keyName) {
        fieldKeyNameMap.put(fieldName, keyName);
    }

    @Override
    public void loadProp(Indicator ind, PropElement eRetractor, ConfigLoadingContext lctxt) {
        super.loadProp(ind, eRetractor, lctxt);
        for (PropElement eFld : eRetractor.getElements("field")) {
            String fieldName = eFld.getProp("name", "");
            String keyName = eFld.getProp("key_name", "");

            if (keyName.isEmpty())
                keyName = fieldName;
            addFieldKeyName(fieldName, keyName);
        }

    }
}
